package com.uobgroup.ews.uam.core.dao;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.persistence.Query;

import org.springframework.stereotype.Repository;

import com.uobgroup.ews.uam.core.domain.AccessControl;
import com.uobgroup.ews.uam.core.domain.CombinationACL;
import com.uobgroup.ews.uam.core.domain.CombinationMatrix;
import com.uobgroup.ews.uam.core.domain.CombinationType;
import com.uobgroup.ews.uam.core.domain.Reference;

@Repository
public class CombinationMatrixDaoImpl extends AbstractJpaDao<CombinationMatrix> implements CombinationMatrixDao {

   public CombinationMatrixDaoImpl() {
      setClazz(CombinationMatrix.class);
   }

   @Override
   public long count() {
      return entityManager.createQuery("SELECT COUNT(o) FROM CombinationMatrix o", Long.class).getSingleResult();
   }

   @Override
   public List<CombinationMatrix> findAll(int firstResult, int maxResults) {
      return entityManager.createQuery("SELECT o FROM CombinationMatrix o", CombinationMatrix.class)
            .setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
   }

   @Override
   public long countCombinationMatrix(AccessControl accessControl) {
      Query query = entityManager.createNamedQuery(CombinationMatrix.COUNT_COMBINATION_BY_ACL);
      query.setParameter(CombinationMatrix.PARA_ACCESSCONTROL, accessControl);
      long count = (Long) query.getSingleResult();
      return count;
   }

   /*
    * Phuong Tran: Update the existing CombinationMatrix entity
    */
   @Override
   public CombinationMatrix update(CombinationMatrix combinationMatrix) {
      CombinationMatrix rtEntity = null;
      // get the existing entity and set the new values to it
      CombinationMatrix existingEntity = this.findOne(combinationMatrix.getId());
      if (existingEntity != null) {
         existingEntity.setCountry(combinationMatrix.getCountry());
         existingEntity.setDescription(combinationMatrix.getDescription());
         existingEntity.setDocumentType(combinationMatrix.getDocumentType());
         //existingEntity.setModificationDate(new Date());
         existingEntity.setProcessingCentre(combinationMatrix.getProcessingCentre());
         existingEntity.setProductType(combinationMatrix.getProductType());
         existingEntity.setTransactionType(combinationMatrix.getTransactionType());
         existingEntity.setUserLocation(combinationMatrix.getUserLocation());

         // merge the new value to existing entity
         entityManager.merge(existingEntity);

      }

      // get and return the updated
      rtEntity = this.findOne(combinationMatrix.getId());
      if (rtEntity == null) {
         rtEntity = new CombinationMatrix();
         // log here to announce update error

      }
      return rtEntity;
   }

   /*
    * Phuong Tran: Delete by Id
    */
   @Override
   public void deleteById(Long id) {
      CombinationMatrix delEntity = this.findOne(id);
      List<CombinationACL> combinationACLs = delEntity.getAccessControls();
      // remove combination
      entityManager.remove(delEntity);
      removeCombinationACLs(combinationACLs);
   }

   /*
    * Phuong Tran: Delete by Id
    */
   @Override
   public void delete(CombinationMatrix delEntity) {
      List<CombinationACL> combinationACLs = delEntity.getAccessControls();
      // remove combination
      entityManager.remove(delEntity);
      removeCombinationACLs(combinationACLs);
   }

   public void removeCombinationACLs(List<CombinationACL> combinationACLs) {
      // remove CombinationACLs
      for (CombinationACL combinationACL : combinationACLs) {
         entityManager.remove(combinationACL);
      }
   }

   /*
    * Phuong Tran: Check the association with Profile
    */
   @Override
   public boolean checkTheAssociationWithProfile(Long Id) {
      boolean rtValue = false;
      Query query = entityManager.createNativeQuery("SELECT * FROM profilecombinationmatrices "
            + "WHERE profilecombinationmatrices.combinationmatrices=" + Id);
      if (query != null) {
         if (query.getResultList().size() > 0) {
            rtValue = true;
         }
      }
      return rtValue;
   }

   /*
    * Phuong Tran
    */
   @Override
   public List<CombinationMatrix> findCombinationsByType(int type) {
      return entityManager.createQuery("FROM CombinationMatrix com WHERE com.combinationType =" + type,
            CombinationMatrix.class).getResultList();
   }

   @SuppressWarnings("unchecked")
   @Override
   public List<CombinationMatrix> findByTypeAndDescription(CombinationType type, String description) {
      String queryStr = "";
      if (description == null) {
         queryStr = "select c from CombinationMatrix c";
         if(type != null){
            queryStr = "select c from CombinationMatrix c where c.combinationType = :combinationType";
         }
      }
      else {
         queryStr = "select c from CombinationMatrix c where (lower(c.description) like :description or c.description is null)";
         if(type != null){
            queryStr = "select c from CombinationMatrix c where (lower(c.description) like :description or c.description is null) and c.combinationType = :combinationType";
         }
         if(description.trim().length() > 0){
            queryStr = queryStr.replaceAll(" or c.description is null", "");
         }
      }
      Query query = entityManager.createQuery(queryStr);
      if (description != null) {
         query.setParameter("description", "%" + description.toLowerCase() + "%");
      }
      if(type != null){
         query.setParameter("combinationType", type);
      }
      return query.getResultList();
   }

   @SuppressWarnings("unchecked")
   @Override
   public boolean isPropertiesExisted(CombinationMatrix combinationMatrix) {
      boolean result = false;
      if (combinationMatrix != null) {
         String queryStr = "";
         CombinationType type = combinationMatrix.getCombinationType();
         Reference countryRef = combinationMatrix.getCountry();
         if (type == CombinationType.CASE) {
            Reference procRef = combinationMatrix.getProcessingCentre();
            Reference transRef = combinationMatrix.getTransactionType();
            queryStr = "select combi from CombinationMatrix combi "
                  + "where combi.combinationType = :type and combi.country = :country and "
                  + "combi.processingCentre = :proc and combi.transactionType = :trans";
            Query query = entityManager.createQuery(queryStr, CombinationMatrix.class);
            query.setParameter("type", type);
            query.setParameter("country", countryRef);
            query.setParameter("proc", procRef);
            query.setParameter("trans", transRef);
            List<CombinationMatrix> lstCombi = query.getResultList();
            if (lstCombi != null) {
               Iterator<CombinationMatrix> it = lstCombi.iterator();
               while (it.hasNext()) {
                  CombinationMatrix combi = it.next();
                  if (checkEqual(combinationMatrix.getProductType(), combi.getProductType())
                        && checkEqual(combinationMatrix.getUserLocation(), combi.getUserLocation())) {
                     result = true;
                     break;
                  }
               }
            }
         }
         else if (type == CombinationType.DOCUMENT) {
            Reference docTypeRef = combinationMatrix.getDocumentType();
            queryStr = "select combi from CombinationMatrix combi "
                  + "where combi.combinationType = :type and combi.country = :country and "
                  + "combi.documentType = :doc";
            Query query = entityManager.createQuery(queryStr, CombinationMatrix.class);
            query.setParameter("type", type);
            query.setParameter("country", countryRef);
            query.setParameter("doc", docTypeRef);
            List<CombinationMatrix> lstCombi = query.getResultList();
            if (lstCombi != null) {
               Iterator<CombinationMatrix> it = lstCombi.iterator();
               while (it.hasNext()) {
                  CombinationMatrix combi = it.next();
                  if (checkEqual(combinationMatrix.getProcessingCentre(), combi.getProcessingCentre())
                        && checkEqual(combinationMatrix.getTransactionType(), combi.getTransactionType())
                        && checkEqual(combinationMatrix.getProductType(), combi.getProductType())
                        && checkEqual(combinationMatrix.getUserLocation(), combi.getUserLocation())) {
                     result = true;
                     break;
                  }
               }
            }
         }
      }
      return result;
   }

   private boolean checkEqual(Reference a, Reference b) {
      if (a == null) {
         return (b == null);
      }
      else {
         return a.equals(b);
      }
   }

   @SuppressWarnings("unchecked")
   @Override
   public List<CombinationMatrix> searchMadatoryDocProperties(String doctype, String country) {
      List<CombinationMatrix> lstCombination = new ArrayList<CombinationMatrix>();
      String queryStr = "select combi from CombinationMatrix combi "
            + "where combi.combinationType = :type and combi.country.name = :country and "
            + "combi.documentType.name = :doc";
      Query query = entityManager.createQuery(queryStr, CombinationMatrix.class);
      query.setParameter("type", CombinationType.DOCUMENT);
      query.setParameter("country", country);
      query.setParameter("doc", doctype);
      lstCombination = query.getResultList();
      return lstCombination;
   }

   /*
    * Phuong Tran
    */
   @SuppressWarnings("unchecked")
   @Override
   public List<CombinationMatrix> searchMadatoryCaseProperties(String processingType, String transType, String country) {
      List<CombinationMatrix> lstCombination = new ArrayList<CombinationMatrix>();
      String queryStr = "select combi from CombinationMatrix combi "
            + "where combi.combinationType = :type and combi.processingCentre.name = :processingCentre and "
            + "combi.transactionType.name = :transactionType and " + "combi.country.name = :country";
      Query query = entityManager.createQuery(queryStr, CombinationMatrix.class);
      query.setParameter("type", CombinationType.CASE);
      query.setParameter("processingCentre", processingType);
      query.setParameter("transactionType", transType);
      query.setParameter("country", country);

      lstCombination = query.getResultList();
      return lstCombination;
   }

   /**
    * @author dongn
    */
   @Override
   public long countCombinationMatrixsThatHasCountryReference(Reference country) {
      return entityManager.createQuery("SELECT COUNT(CB) FROM CombinationMatrix CB WHERE CB.country = :country", Long.class)
            .setParameter("country", country).getSingleResult();
   }

   @Override
   public long countCombinationMatrixsThatHasDocumentTypeReference(Reference documentType) {
      return entityManager.createQuery("SELECT COUNT(CB) FROM CombinationMatrix CB WHERE CB.documentType = :documentType", Long.class)
            .setParameter("documentType", documentType).getSingleResult();
   }

   @Override
   public long countCombinationMatrixsThatHasProductTypeReference(Reference productType) {
      return entityManager.createQuery("SELECT COUNT(CB) FROM CombinationMatrix CB WHERE CB.productType = :productType", Long.class)
            .setParameter("productType", productType).getSingleResult();
   }

   @Override
   public long countCombinationMatrixsThatHasTransactionTypeReference(Reference transactionType) {
      return entityManager.createQuery("SELECT COUNT(CB) FROM CombinationMatrix CB WHERE CB.transactionType = :transactionType", Long.class)
            .setParameter("transactionType", transactionType).getSingleResult();
   }

   @Override
   public long countCombinationMatrixsThatHasProcessingCenterReference(Reference processingCentre) {
      return entityManager.createQuery("SELECT COUNT(CB) FROM CombinationMatrix CB WHERE CB.processingCentre = :processingCentre", Long.class)
            .setParameter("processingCentre", processingCentre).getSingleResult();
   }

   @Override
   public long countCombinationMatrixsThatHasUserLocationReference(Reference userLocation) {
      return entityManager.createQuery("SELECT COUNT(CB) FROM CombinationMatrix CB WHERE CB.userLocation = :userLocation", Long.class)
            .setParameter("userLocation", userLocation).getSingleResult();
   }

}
